package com.example.riceexambackend.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;
import com.example.riceexambackend.common.errorcode.ServiceErrorCodeConstants;
import com.example.riceexambackend.common.exception.ServiceException;
import com.example.riceexambackend.common.utils.JWTUtil;
import com.example.riceexambackend.controller.param.exam.ExamAddParam;
import com.example.riceexambackend.controller.param.exam.ExamAttendParam;
import com.example.riceexambackend.controller.param.exam.ExamAutoParam;
import com.example.riceexambackend.controller.param.exam.ExamSubmitParam;
import com.example.riceexambackend.controller.param.questiontype.QuestionTypeAddParam;
import com.example.riceexambackend.controller.param.questiontype.QuestionTypeDeleteParam;
import com.example.riceexambackend.controller.param.questiontype.QuestionTypeUpdateParam;
import com.example.riceexambackend.dao.dataobject.*;
import com.example.riceexambackend.dao.mapper.*;
import com.example.riceexambackend.service.ExamService;
import com.example.riceexambackend.service.QuestionTypeService;
import com.example.riceexambackend.service.dto.exam.*;
import com.example.riceexambackend.service.dto.questiontype.QuestionTypeAddDTO;
import com.example.riceexambackend.service.dto.questiontype.QuestionTypeDeleteDTO;
import com.example.riceexambackend.service.dto.questiontype.QuestionTypeSelectDTO;
import com.example.riceexambackend.service.dto.questiontype.QuestionTypeUpdateDTO;
import io.jsonwebtoken.Claims;
import jakarta.annotation.Resource;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class ExamServiceImpl implements ExamService {

    @Resource
    private ExamMapper examMapper;
    @Resource
    private UserMapper userMapper;
    @Resource
    private QuestionMapper questionMapper;
    @Resource
    private ExamQuestionMapper examQuestionMapper;
    @Resource
    private QuestionTypeMapper questionTypeMapper;
    @Resource
    private AnswerMapper answerMapper;

    @Override
    public ExamSubmitDTO submitExam(ExamSubmitParam examSubmitParam) {
        // 1. 参数非空校验
        Long examId = examSubmitParam.getExamId();
        String token = examSubmitParam.getToken();
        Map<String, String> answers = examSubmitParam.getAnswers();
        if (examId == null || examId <= 0) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_SUBMIT_PARAM_NULL);
        }
        if (StringUtils.isBlank(token)) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_SUBMIT_PARAM_NULL);
        }
        if (CollUtil.isEmpty(answers)) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_SUBMIT_PARAM_NULL);
        }
        // 2. 具体参数校验
        Claims claims = JWTUtil.parseJWT(token);
        long userId = (int) claims.get("id");
        if (userId <= 0) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_SUBMIT_PARAM_NULL);
        }
        UserDO dbUser = userMapper.selectById(userId);
        if (dbUser == null) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_SUBMIT_PARAM_ERROR);
        }
        ExamDO dbExam = examMapper.selectById(examId);
        if (dbExam == null) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_SUBMIT_PARAM_ERROR);
        }
        LocalDateTime startTime = dbExam.getExamStartTime();
        LocalDateTime endTime = dbExam.getExamEndTime();
        if (LocalDateTime.now().isBefore(startTime)) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_SUBMIT_PARAM_ERROR);
        }
        if (LocalDateTime.now().isAfter(endTime)) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_SUBMIT_PARAM_ERROR);
        }
        // 3. 插入考试结果
        for (Map.Entry<String, String> answer : answers.entrySet()) {
            long questionId = Long.parseLong(answer.getKey());
            String userAnswer = answer.getValue();
            AnswerDO answerDO = new AnswerDO();
            answerDO.setUserId(userId);
            answerDO.setQuestionId(questionId);
            answerDO.setExamId(examId);
            answerDO.setUserAnswer(userAnswer);
            answerMapper.insertAnswer(answerDO);
        }
        // 4. 封装响应
        ExamSubmitDTO examSubmitDTO = new ExamSubmitDTO();
        examSubmitDTO.setSuccess(true);
        return examSubmitDTO;
    }

    @Override
    public List<ExamAttendDTO> attendExam(ExamAttendParam examAttendParam) {
        // 1. examId、token参数非空校验
        Long examId = examAttendParam.getExamId();
        String token = examAttendParam.getToken();
        if (examId == null || examId <= 0) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_ATTEND_PARAM_NULL);
        }
        if (StringUtils.isBlank(token)) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_ATTEND_PARAM_NULL);
        }
        // 2. 校验用户是否登录
        Claims claims = JWTUtil.parseJWT(token);
        int userId = (int) claims.get("id");
        UserDO dbUser = userMapper.selectById((long)userId);
        if (dbUser == null) {
            throw new ServiceException(500, "用户未登录!");
        }
        // 3. 校验考试id是否存在
        ExamDO dbExam = examMapper.selectById(examId);
        if (dbExam == null) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_ATTEND_PARAM_ERROR);
        }
        // 4. 校验当前时间是否 <= 考试截止时间
        LocalDateTime endTime = dbExam.getExamEndTime();
        if (LocalDateTime.now().isAfter(endTime)) {
            // 考试已经截止
            throw new ServiceException(500, "考试已经截止!");
        }
        // 4. 校验当前时间是否 >= 考试开始时间
        LocalDateTime startTime = dbExam.getExamStartTime();
        if (LocalDateTime.now().isBefore(startTime)) {
            // 考试已经截止
            throw new ServiceException(500, "考试还未开始!");
        }
        // 5. 查询examId对应题目信息
        List<ExamAttendDTO> examAttendDTOList = new ArrayList<>();
        List<ExamQuestionDO> examQuestionDOList = examQuestionMapper.selectByExamId(examId);
        for (ExamQuestionDO examQuestionDO : examQuestionDOList) {
            Long questionId = examQuestionDO.getQuestionId();
            QuestionDO questionDO = questionMapper.selectById(questionId);
            ExamAttendDTO examAttendDTO = new ExamAttendDTO();
            examAttendDTO.setQuestionId(questionDO.getId());
            examAttendDTO.setQuestionTypeId(questionDO.getQuestionTypeId());
            examAttendDTO.setQuestionName(questionDO.getQuestionName());
            examAttendDTO.setOptionA(questionDO.getOptionA());
            examAttendDTO.setOptionB(questionDO.getOptionB());
            examAttendDTO.setOptionC(questionDO.getOptionC());
            examAttendDTO.setOptionD(questionDO.getOptionD());
            examAttendDTOList.add(examAttendDTO);
        }
        // 6. 返回响应
        return examAttendDTOList;
    }

    @Override
    public List<ExamViewDTO> viewExam(String examIdStr) {
        // 1. 校验id
        Long examId = Long.parseLong(examIdStr);
        if (examId <= 0) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_VIEW_PARAM_ERROR);
        }
        // 2. 校验id是否存在
        List<ExamQuestionDO> examQuestionDOList = examQuestionMapper.selectByExamId(examId);
        if (CollUtil.isEmpty(examQuestionDOList)) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_VIEW_PARAM_ERROR);
        }
        // 3. 根据题目id关联查询全部题目
        List<ExamViewDTO> examViewDTOList = new ArrayList<>();
        for (ExamQuestionDO examQuestionDO : examQuestionDOList) {
            ExamViewDTO examViewDTO = new ExamViewDTO();
            Long questionId = examQuestionDO.getQuestionId();
            // 3.1 关联查询题目信息
            QuestionDO questionDO = questionMapper.selectById(questionId);
            examViewDTO.setQuestionId(questionDO.getId());
            examViewDTO.setQuestionName(questionDO.getQuestionName());
            examViewDTO.setOptionA(questionDO.getOptionA());
            examViewDTO.setOptionB(questionDO.getOptionB());
            examViewDTO.setOptionC(questionDO.getOptionC());
            examViewDTO.setOptionD(questionDO.getOptionD());
            examViewDTO.setRightAnswer(questionDO.getRightAnswer());
            Long questionTypeId = questionDO.getQuestionTypeId();
            // 3.2 关联查询题型信息
            QuestionTypeDO questionTypeDO = questionTypeMapper.selectById(questionTypeId);
            examViewDTO.setQuestionTypeName(questionTypeDO.getQuestionName());
            examViewDTOList.add(examViewDTO);
        }
        // 4. 返回响应
        return examViewDTOList;
    }

    @Transactional // 事务管理
    @Override
    public ExamAutoDTO autoGenerateExamQuestion(ExamAutoParam param) {
        // 1. 参数非空校验
        Long examId = param.getExamId();
        Integer singleCount = param.getSingleCount();
        Integer judgeCount = param.getJudgeCount();
        Integer subjectCount = param.getSubjectCount();
        if (examId == null) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_AUTO_PARAM_NULL);
        }
        if (singleCount == null) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_AUTO_PARAM_NULL);
        }
        if (subjectCount == null) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_AUTO_PARAM_NULL);
        }
        if (subjectCount == null) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_AUTO_PARAM_NULL);
        }
        // 2. 具体参数校验
        // 2.1 校验examId是否存在
        ExamDO examDO = examMapper.selectById(examId);
        if (examDO == null) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_AUTO_PARAM_ERROR);
        }
        // 2.2 查询数据库全部题目
        List<QuestionDO> questionList = questionMapper.selectList();
        List<QuestionDO> singleQuestionList = questionList.stream()
                .filter((question) -> question.getQuestionTypeId() == 1)
                .collect(Collectors.toList());
        List<QuestionDO> judgeQuestionList = questionList.stream()
                .filter((question) -> question.getQuestionTypeId() == 3)
                .collect(Collectors.toList());
        List<QuestionDO> subjectQuestionList = questionList.stream()
                .filter((question) -> question.getQuestionTypeId() == 6)
                .collect(Collectors.toList());
        // 2.3 校验数量是否合法
        int singleQuestionSize = singleQuestionList.size();
        int judgeQuestionSize = judgeQuestionList.size();
        int subjectQuestionSize = subjectQuestionList.size();
        if (singleCount > singleQuestionSize) {
            throw new ServiceException(500, "单选题数量不足!");
        }
        if (judgeCount > judgeQuestionSize) {
            throw new ServiceException(500, "判断题数量不足!");
        }
        if (subjectCount > subjectQuestionSize) {
            throw new ServiceException(500, "简答题数量不足!");
        }
        // 2.4 随机选题
        List<QuestionDO> hasChooseQuestionList = new ArrayList<>();
        hasChooseQuestionList.addAll(randomChooseQuestion(singleQuestionList, singleCount));
        hasChooseQuestionList.addAll(randomChooseQuestion(judgeQuestionList, judgeCount));
        hasChooseQuestionList.addAll(randomChooseQuestion(subjectQuestionList, subjectCount));
        // 3. 删除原先组卷记录
        examQuestionMapper.deleteByExamId(examId);
        // 4. 批量插入组卷记录
        for (QuestionDO questionDO : hasChooseQuestionList) {
            ExamQuestionDO examQuestionDO = new ExamQuestionDO();
            examQuestionDO.setQuestionId(questionDO.getId());
            examQuestionDO.setExamId(examId);
            examQuestionMapper.insertExam(examQuestionDO);
        }
        // 5. 计算总分和总题目数
        int examTotalCount = singleCount + judgeCount + subjectCount;
        int singleScore = questionTypeMapper.selectScoreByName("单选题");
        int judgeScore = questionTypeMapper.selectScoreByName("判断题");
        int subjectScore = questionTypeMapper.selectScoreByName("简答题");
        int examTotalScore = singleCount * singleScore + subjectCount * judgeScore + subjectCount * subjectScore;
        ExamDO updateExam = new ExamDO();
        updateExam.setId(examId);
        updateExam.setExamTotalScore(examTotalScore);
        updateExam.setExamTotalCount(examTotalCount);
        examMapper.updateTotalScoreAndTotalCountById(updateExam);
        // 6. 返回响应
        ExamAutoDTO examAutoDTO = new ExamAutoDTO();
        examAutoDTO.setSuccess(true);
        return examAutoDTO;
    }

    private List<QuestionDO> randomChooseQuestion(List<QuestionDO> sourceQuestionList, int requireCount) {
        return RandomUtil.randomEleList(sourceQuestionList, requireCount);
    }

    @Override
    public List<ExamSelectDTO> selectAllExam() {
        // 1. 查询数据库全部试卷数据
        List<ExamDO> examDOList = examMapper.selectList();
        // 2. 关联 examUserId 查询出卷人
        List<ExamSelectDTO> examSelectDTOList = new ArrayList<>();
        for (ExamDO examDO : examDOList) {
            Long examUserId = examDO.getExamUserId(); // 出卷人id
            UserDO examUser = userMapper.selectById(examUserId);
            // 脱敏处理
            examUser = getMaskUser(examUser);
            ExamSelectDTO examSelectDTO = new ExamSelectDTO();
            examSelectDTO.setExamId(examDO.getId());
            examSelectDTO.setExamName(examDO.getExamName());
            examSelectDTO.setExamStartTime(examDO.getExamStartTime());
            examSelectDTO.setExamEndTime(examDO.getExamEndTime());
            examSelectDTO.setExamDuration(examDO.getExamDuration());
            examSelectDTO.setExamTotalScore(examDO.getExamTotalScore());
            examSelectDTO.setExamTotalCount(examDO.getExamTotalCount());
            examSelectDTO.setExamUser(examUser);
            examSelectDTO.setExamStatus(examDO.getExamStatus());
            examSelectDTOList.add(examSelectDTO);
        }
        // 3. 返回响应
        return examSelectDTOList;
    }

    /**
     * 获得脱敏用户
     * @param srcUserDO 原始UserDO
     * @return
     */
    private UserDO getMaskUser(UserDO srcUserDO) {
        UserDO retDO = new UserDO();
        retDO.setId(srcUserDO.getId());
        retDO.setUserName(srcUserDO.getUserName());
        retDO.setUserAccount(srcUserDO.getUserAccount());
        return retDO;
    }

    @Override
    public ExamAddDTO insertExam(ExamAddParam param) {
        // 1. 试卷名称 上传用户 开始时间 结束时间非空校验
        String examName = param.getExamName();
        Long examUserId = param.getExamUserId();
        LocalDateTime examStartTime = param.getExamStartTime();
        LocalDateTime examEndTime = param.getExamEndTime();
        if (StringUtils.isBlank(examName)) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_ADD_PARAM_NULL);
        }
        if (examUserId == null) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_ADD_PARAM_NULL);
        }
        if (examStartTime == null) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_ADD_PARAM_NULL);
        }
        if (examEndTime == null) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_ADD_PARAM_NULL);
        }
        // 2. 参数具体校验
        if (LocalDateTime.now().isAfter(examStartTime)) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_ADD_PARAM_ERROR);
        }
        if (examStartTime.isAfter(examEndTime)) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_ADD_PARAM_ERROR);
        }
        if (examName.length() < 1 || examName.length() > 64) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_ADD_PARAM_ERROR);
        }
        if (examUserId <= 0) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_ADD_PARAM_ERROR);
        }
        // 3. 查询上传用户
        UserDO dbDO = userMapper.selectById(examUserId);
        if (dbDO == null) {
            throw new ServiceException(ServiceErrorCodeConstants.EXAM_ADD_PARAM_ERROR);
        }
        // 4. 计算duration
        int duration = (int) (examEndTime.toEpochSecond(ZoneOffset.of("+8")) - examStartTime.toEpochSecond(ZoneOffset.of("+8"))) / 60;
        ExamDO examDO = new ExamDO();
        examDO.setExamName(examName);
        examDO.setExamStartTime(examStartTime);
        examDO.setExamEndTime(examEndTime);
        examDO.setExamDuration(duration);
        examDO.setExamUserId(examUserId);
        // 5. 存入数据库当中
        examMapper.insertExam(examDO);
        // 6. 返回响应
        ExamAddDTO examAddDTO = new ExamAddDTO();
        examAddDTO.setExamId(examDO.getId());
        return examAddDTO;
    }

//    @Override
//    public QuestionTypeDeleteDTO deleteQuestionType(QuestionTypeDeleteParam param) {
//        // 1. 题型id非空校验
//        Long questionTypeId = param.getQuestionTypeId();
//        if (questionTypeId == null) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_DELETE_PARAM_NULL);
//        }
//        // 2. 具体参数校验
//        if (questionTypeId <= 0) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_DELETE_PARAM_ERROR);
//        }
//        // 3. 查询待删除记录是否存在
//        QuestionTypeDO dbDO = questionTypeMapper.selectById(questionTypeId);
//        if (dbDO == null) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_DELETE_PARAM_ERROR);
//        }
//        // 4. 逻辑删除数据库记录
//        questionTypeMapper.deleteById(questionTypeId);
//        // 5. 返回响应
//        QuestionTypeDeleteDTO questionTypeDeleteDTO = new QuestionTypeDeleteDTO();
//        questionTypeDeleteDTO.setQuestionTypeId(questionTypeId);
//        return questionTypeDeleteDTO;
//    }
//
//    @Override
//    public QuestionTypeUpdateDTO updateQuestionType(QuestionTypeUpdateParam param) {
//        // 1. 题型id、题型名称、题型分数非空校验
//        Long questionTypeId = param.getQuestionTypeId();
//        String questionTypeName = param.getQuestionName();
//        Byte questionTypeScore = param.getQuestionScore();
//        if (questionTypeId == null) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_UPDATE_PARAM_NULL);
//        }
//        if (StringUtils.isBlank(questionTypeName)) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_UPDATE_PARAM_NULL);
//        }
//        if (questionTypeScore == null) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_UPDATE_PARAM_NULL);
//
//        }
//        // 2. 具体校验
//        if (questionTypeName.length() > 8) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_UPDATE_PARAM_ERROR);
//        }
//        if (questionTypeScore > 100 || questionTypeScore <= 0) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_UPDATE_PARAM_ERROR);
//        }
//        if (questionTypeId <= 0) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_UPDATE_PARAM_ERROR);
//        }
//        QuestionTypeDO dbDO = questionTypeMapper.selectById(questionTypeId);
//        if (dbDO == null) {
//            // 修改记录不存在
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_UPDATE_PARAM_ERROR);
//        }
//        // 3. 修改数据库
//        QuestionTypeDO questionTypeDO = new QuestionTypeDO();
//        questionTypeDO.setId(questionTypeId);
//        questionTypeDO.setQuestionName(questionTypeName);
//        questionTypeDO.setQuestionScore(questionTypeScore);
//        questionTypeMapper.updateById(questionTypeDO);
//        QuestionTypeUpdateDTO questionTypeUpdateDTO = new QuestionTypeUpdateDTO();
//        questionTypeUpdateDTO.setQuestionTypeId(questionTypeId);
//        return questionTypeUpdateDTO;
//    }
//
//    @Override
//    public List<QuestionTypeSelectDTO> selectAllQuestionType() {
//        // 查询全部数据
//        List<QuestionTypeDO> questionTypeDOList = questionTypeMapper.selectList();
//        // 返回给前端
//        List<QuestionTypeSelectDTO> questionTypeSelectDTOList = new ArrayList<>();
//        for (QuestionTypeDO questionTypeDO : questionTypeDOList) {
//            QuestionTypeSelectDTO questionTypeSelectDTO = new QuestionTypeSelectDTO();
//            questionTypeSelectDTO.setId(questionTypeDO.getId());
//            questionTypeSelectDTO.setQuestionName(questionTypeDO.getQuestionName());
//            questionTypeSelectDTO.setQuestionScore(questionTypeDO.getQuestionScore());
//            questionTypeSelectDTOList.add(questionTypeSelectDTO);
//        }
//        return questionTypeSelectDTOList;
//    }
//
//    @Override
//    public QuestionTypeAddDTO insertQuestionType(QuestionTypeAddParam param) {
//        // 0. 参数校验
//        if (param == null) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_ADD_PARAM_NULL);
//        }
//        // 1. 题型名称、题型分数非空校验
//        String questionTypeName = param.getQuestionName().trim();
//        byte questionTypeScore = param.getQuestionScore();
//        if (StringUtils.isBlank(questionTypeName)) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_ADD_PARAM_ERROR);
//        }
//        // 2. 题型名称 <= 8位
//        if (questionTypeName.length() > 8) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_ADD_PARAM_ERROR);
//        }
//        // 3. 1 <= 题型分数 <= 100
//        if (questionTypeScore < 1 || questionTypeScore > 100) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_ADD_PARAM_ERROR);
//        }
//        // 4. 题型名称不得重复
//        QuestionTypeDO dbDO = questionTypeMapper.selectByName(questionTypeName);
//        if (dbDO != null) {
//            throw new ServiceException(ServiceErrorCodeConstants.QUESTION_TYPE_ADD_NAME_EXISTS);
//        }
//        // 5. 插入到数据库中
//        QuestionTypeDO questionTypeDO = new QuestionTypeDO();
//        questionTypeDO.setQuestionName(questionTypeName);
//        questionTypeDO.setQuestionScore(questionTypeScore);
//        questionTypeMapper.insertQuestionType(questionTypeDO);
//        // 6. 返回响应
//        QuestionTypeAddDTO questionTypeAddDTO = new QuestionTypeAddDTO();
//        questionTypeAddDTO.setQuestionTypeId(questionTypeDO.getId());
//        return questionTypeAddDTO;
//    }
}
